#include "WeakObjectPtr.h"
#include "Class.h"

#include "GenericInfo.h"
#include "PlaceholderType.h"

#include "Containers/WeakObjectPtr.h"

#include "Object/Object.h"

#include "TypeInstance.h"

CString CWeakObjectPtrType::PlaceholderName = U"T";

CWeakObjectPtrType::CWeakObjectPtrType()
    :CType(
        STypeID(CType::GetEngineCoreNamespace()+U"::TWeakObjectPtr",true)
        ,ETypeFlags::Generic | ETypeFlags::WeakObjectPtr | ETypeFlags::CantUseInScript
       )
{
    GenericInfo = new CGenericInfo();
    auto PlaceholderType = new CPlaceholderType(
        STypeID(GetFullName()+U"::"+PlaceholderName,true)
        , ETypeFlags::None
        , 0
        ,this
        );
    GenericInfo->PlaceholderTypes.Add(PlaceholderType);
}

CWeakObjectPtrType * CreateWeakObjectPtrType()
{
    CWeakObjectPtrType *WeakObjectPtrType = new CWeakObjectPtrType();
    return WeakObjectPtrType;
}

CWeakObjectPtrType *CWeakObjectPtrType::GetStaticType()
{
    static CWeakObjectPtrType *StaticType = CreateWeakObjectPtrType();
    return StaticType;
}

CString CWeakObjectPtrType::CreateTypeName(const CString &ElementType)
{
    return STypeID::CreateTemplateTypeName(
        GetStaticType()->GetFullName()
        ,{ElementType}
        ,{PlaceholderName}
        );
}

STypeID CWeakObjectPtrType::CreateID(const SDecoratedType &ElementType)
{
    return STypeID(
        GetStaticType()->ID
        ,{ElementType.BasicType->ID}
        ,{PlaceholderName}
        );
}

static uint8_t RegisterWeakObjectPtr=(CWeakObjectPtrType::GetStaticType(),0); 

class CWeakObjectPtrInstance : public ITypeInstance
{
protected:
    CWeakObjectPtrTypeGeneralized * Type;
public:
    CWeakObjectPtrInstance(CWeakObjectPtrTypeGeneralized * InType)
        :Type(InType)
    {
    }

    bool IsForceConvertableTo(CType * Other) override
    {

        if(!Other->IsWeakObjectPtrType())
        {
            return false;
        }

        return Type->GetObjectClass()->CanObjectConvertTo( 
        reinterpret_cast<CWeakObjectPtrTypeGeneralized*>(Other)->GetObjectClass());
    }

    virtual uint32_t GetSize() override
    {
        return sizeof(TWeakObjectPtr<RObject>);
    }

    virtual void InitInstance(void * Memory) override
    {
        new (Memory) TWeakObjectPtr<RObject>();
    }

    virtual void EraseInstance(void * Instance) override
    {
        reinterpret_cast<TWeakObjectPtr<RObject>*>(Instance)->~TWeakObjectPtr<RObject>();
    }

    virtual void CloneInstance(const void * Instance, void * TargetMemory) override
    {
        new (TargetMemory) TWeakObjectPtr<RObject>(*(const TWeakObjectPtr<RObject> *)Instance);
    }

    virtual uint64_t GetInstanceHash(const void * InInstance) const override
    {
        return ((const TWeakObjectPtr<RObject> *)InInstance)->GetHashCode();
    }

    virtual bool IsInstanceEqual(const void * A,const void * B) const override
    {
        return *((const TWeakObjectPtr<RObject> *)A)==*((const TWeakObjectPtr<RObject> *)B);
    }

    virtual CString ToDebugString(const void * InInstance) const override
    {
        const TWeakObjectPtr<RObject> *Instance=reinterpret_cast<const TWeakObjectPtr<RObject>*>(InInstance);
        if(Instance->IsValid())
        {
            return Type->GetObjectClass()->ToDebugString(Instance->Get());
        }
        else
        {
            return U"nullptr";
        }
    }

    virtual void * CreateInstance() override
    {
        return new TWeakObjectPtr<RObject>();
    }

    virtual void ResetAllocated(void * Memory) override
    {
        reinterpret_cast<TWeakObjectPtr<RObject>*>(Memory)->Clear();
    }

};

CWeakObjectPtrTypeGeneralized::CWeakObjectPtrTypeGeneralized(CClass *InClass)
   :CType(
        CWeakObjectPtrType::CreateID(InClass)
        ,ETypeFlags::WeakObjectPtr | ETypeFlags::Generalized | ETypeFlags::CantUseInScript
    )
{
    Instancable =new CWeakObjectPtrInstance(this);

    GeneralizedInfo=new CGeneralizedInfo();
    GeneralizedInfo->TemplateType=CWeakObjectPtrType::GetStaticType();
    GeneralizedInfo->TypeArguments.Add(InClass);

    GeneralizedInfo->TemplateType->GenericInfo->GeneralizedTypes.Add(this);
    reinterpret_cast<CGenericInfo*>(GeneralizedInfo->TemplateType->GenericInfo)
        ->GenericMap.Add(TVector<CType*>({InClass}),this);
    
}

CClass *CWeakObjectPtrTypeGeneralized::GetObjectClass() const
{
    return reinterpret_cast<CClass*>(GeneralizedInfo->TypeArguments[0]);
}

void CWeakObjectPtrTypeGeneralized::SetObjectPtr(void *Instance, RObject *InObject)
{
    TWeakObjectPtr<RObject> *Value=reinterpret_cast<TWeakObjectPtr<RObject>*>(Instance);
    *Value=InObject;
}

RObject *CWeakObjectPtrTypeGeneralized::GetObjectPtr(void *Instance)
{
    TWeakObjectPtr<RObject> *Value=reinterpret_cast<TWeakObjectPtr<RObject>*>(Instance);
    return Value->Get();
}
